package com.cn.delan.core.util;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

import javax.imageio.ImageIO;
/**
 * 图像工具类<br/>
 * 实现图像的读、写、波浪化、色值变换、透明化、加水印、克隆等操作
 * @author BingGo
 *
 */
public class ImageUtil {
	/**
	 * 将BufferedImage对象中的图书出到对应的输出流中
	 * @param bi 图像对象
	 * @param formatname 图像输出格式
	 * @param out 输出流
	 * @throws ImageFormatException 图像格式错误时抛出此异常
	 * @throws IOException IO操作错误时抛出此异常
	 */
	public static void write(BufferedImage bi,String formatname,OutputStream out) throws IOException{
		ImageIO.write(bi, formatname, out);
	}
	/**
	 * 将BufferedImage对象中的图书出到对应的文件中
	 * @param bi 图像对象
	 * @param formatname 图像输出格式
	 * @param file 文件
	 * @throws ImageFormatException 图像格式错误时抛出此异常
	 * @throws IOException IO操作错误时抛出此异常
	 */
	public static void write(BufferedImage bi,String formatname,File file) throws IOException{
		ImageIO.write(bi, formatname, file);;
	}
	/**
	 * 将BufferedImage对象中的图书出到对应路径的文件中
	 * @param bi 图像对象
	 * @param formatname 图像输出格式
	 * @param path 文件路径
	 * @throws ImageFormatException 图像格式错误时抛出此异常
	 * @throws IOException IO操作错误时抛出此异常
	 */
	public static void write(BufferedImage bi,String formatname,String path) throws IOException{
		File file = new File(path);
		ImageUtil.write(bi,formatname, file);
	}
	/**
	 * 通过输入流创建图像
	 * @param input 输入流
	 * @return 产生的图像
	 * @throws IOException
	 */
	public static BufferedImage read(InputStream input) throws IOException{
		return ImageIO.read(input);
	}
	/**
	 * 通过文件创建图像
	 * @param file 文件
	 * @return 产生的图像
	 * @throws IOException
	 */
	public static BufferedImage read(File file) throws IOException{
		return ImageIO.read(file);
	}
	/**
	 * 通过URL创建图像
	 * @param url URL地址
	 * @return 产生的图像
	 * @throws IOException
	 */
	public static BufferedImage read(URL url) throws IOException{
		return ImageIO.read(url);
	}
	/**
	 * 通过文件路径创建图像
	 * @param path 文件路径
	 * @return 产生的图像
	 * @throws IOException
	 */
	public static BufferedImage read(String path) throws IOException{
		File file = new File(path);
		return ImageUtil.read(file);
	}
	/**
	 * 图像波浪化
	 * @param bi 需要处理的图像
	 * @param max 波浪峰值
	 * @return 处理后的图像
	 */
	public static BufferedImage turnWave(BufferedImage bi,int max){
		int width = bi.getWidth();
		int height = bi.getHeight();
		Graphics g = bi.getGraphics();
		//左平移,移动量随机
		double rand = Math.random()*Math.PI+1;
		for(int i=0;i<width;i++){
			double d = max*Math.sin(i/(double)width*Math.PI*2+rand);
			g.copyArea(i, 0, 1, height, 0, (int) d);
		}
		for(int i=0;i<height;i++){
			double d = max*Math.sin(i/(double)height*Math.PI*2+rand);
			g.copyArea(0, i, width, 1, (int) d, 0);
		}
		return bi;
	}
	/**
	 * 将图像与指定颜色进行或运算
	 * @param bi 需要处理的图像
	 * @param color 指定颜色
	 * @return 处理后的图像
	 */
	public static BufferedImage turnOrColor(BufferedImage bi,Color color){
		int width = bi.getWidth();
		int height = bi.getHeight();
		int rgb = color.getRGB();
		int rgbArray[] = new int[width*height];
		rgbArray = bi.getRGB(0, 0, width, height, rgbArray, 0, width);
		for(int i=0;i<rgbArray.length;i++){
			rgbArray[i] = rgb|rgbArray[i];
		}
		bi.setRGB(0, 0, width, height, rgbArray, 0, width);
		return bi;
	}
	/**
	 * 将图像与指定颜色进行与运算
	 * @param bi 需要处理的图像
	 * @param color 指定颜色
	 * @return 处理后的图像
	 */
	public static BufferedImage turnAndColor(BufferedImage bi,Color color){
		int width = bi.getWidth();
		int height = bi.getHeight();
		int rgb = color.getRGB();
		int rgbArray[] = new int[width*height];
		rgbArray = bi.getRGB(0, 0, width, height, rgbArray, 0, width);
		for(int i=0;i<rgbArray.length;i++){
			rgbArray[i] = rgb&rgbArray[i];
		}
		bi.setRGB(0, 0, width, height, rgbArray, 0, width);
		return bi;
	}
	/**
	 * 将图像与指定颜色进行异或运算
	 * @param bi 需要处理的图像
	 * @param color 指定颜色
	 * @return 处理后的图像
	 */
	public static BufferedImage turnXorColor(BufferedImage bi,Color color){
		int width = bi.getWidth();
		int height = bi.getHeight();
		int rgb = color.getRGB();
		int rgbArray[] = new int[width*height];
		rgbArray = bi.getRGB(0, 0, width, height, rgbArray, 0, width);
		for(int i=0;i<rgbArray.length;i++){
			rgbArray[i] = rgb^rgbArray[i];
		}
		bi.setRGB(0, 0, width, height, rgbArray, 0, width);
		return bi;
	}
	/**
	 * 对图像各像素进行非运算
	 * @param bi 需要处理的图像
	 * @return 处理后的图像
	 */
	public static BufferedImage turnNotColor(BufferedImage bi){
		int width = bi.getWidth();
		int height = bi.getHeight();
		int rgbArray[] = new int[width*height];
		rgbArray = bi.getRGB(0, 0, width, height, rgbArray, 0, width);
		for(int i=0;i<rgbArray.length;i++){
			rgbArray[i] = ~rgbArray[i];
		}
		bi.setRGB(0, 0, width, height, rgbArray, 0, width);
		return bi;
	}
	/**
	 * 对图像进行不透明度变化,并将白色进行透明处理即白色不透明度为0
	 * @param bi 需要变化的图像
	 * @param alpha 不透明度的值 0-255，数值越大不透明度越高，数值越小透明度越高
	 * @return 透明化后的图像
	 */
	public static BufferedImage turnAlpha(BufferedImage bi,int alpha){
		return ImageUtil.turnAlpha(bi, alpha, true);
	}
	/**
	 * 对图像进行不透明度变化
	 * @param bi 需要变化的图像
	 * @param alpha 不透明度的值 0-255，数值越大不透明度越高，数值越小透明度越高
	 * @param isGlass 是否玻璃化，即当为真时白色转换为完全透明，不透明度值为0
	 * @return 透明化后的图像
	 */
	public static BufferedImage turnAlpha(BufferedImage bi,int alpha,boolean isGlass){
		int width = bi.getWidth();
		int height = bi.getHeight();
		//色值数组
		int rgbArray[] = new int[width*height];
		int alphaInt = ((alpha & 0xff) << 24);
		rgbArray = bi.getRGB(0, 0, width, height, rgbArray, 0, width);
		for(int i=0;i<rgbArray.length;i++){
			rgbArray[i] = rgbArray[i] & 0x00ffffff;
			//不是白色或者isGlass等于false时就进行alpha设置
			if(rgbArray[i]!=0x00ffffff || !isGlass){
				rgbArray[i]|=alphaInt;
			}
		}
		BufferedImage nbi = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
		nbi.setRGB(0, 0, width, height, rgbArray, 0, width);
		return nbi;
	}
	/**
	 * 图像缩放
	 * @param bi 需要处理的图像
	 * @param sx 横向缩放比例，0~1之间
	 * @param sy 纵向缩放比例，0~1之间
	 * @return 处理后的图像
	 */
	public static BufferedImage resize(BufferedImage bi,double sx,double sy){
		 AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(sx,sy),null);
		 bi = op.filter(bi,null);
		 return bi;
	}
	/**
	 * 图像等比缩小
	 * @param bi 需要处理的图像
	 * @param max 最大边长
	 * @return 处理后的图像
	 */
	public static BufferedImage resize(BufferedImage bi,int max){
		int cmax = bi.getWidth();
		if(cmax < bi.getHeight()){
			cmax = bi.getHeight();
		}
		double x = (double)max/(double)cmax;
		//禁止图片放大
		if(x>1){
			x = 1;
		}
		bi = resize(bi,x,x);
		return bi;
	}
	/**
	 * 批量图像等比缩小,已存在图片不覆盖
	 * @param sfile 需要缩放的图像
	 * @param dfile 缩放后的图像
	 * @param formatname png jpeg
	 * @param max 最大边长
	 * @throws IOException
	 */
	public static void resize(String spath,String dpath,String formatname,int max) throws IOException{
		resize(new File(spath),new File(dpath),formatname,max);
	}
	/**
	 * 批量图像等比缩小,已存在图片不覆盖
	 * @param sfile 需要缩放的图像
	 * @param dfile 缩放后的图像
	 * @param formatname png jpeg
	 * @param max 最大边长
	 * @throws IOException
	 */
	public static void resize(File sfile,File dfile,String formatname,int max) throws IOException{
		if(sfile.exists()){
			if(sfile.isFile()){
				String filename = sfile.getName().toLowerCase();
				//目标文件已存在不处理
				if(dfile.exists()){
					return;
				}
				//非图像不处理
				if(!filename.endsWith("jpg") && !filename.endsWith("jpeg") && !filename.endsWith("gif") && !filename.endsWith("png")){
					return;
				}
				BufferedImage bi = read(sfile);
				bi = resize(bi,max);
				//目标路径不存在新建上层文件夹
				if(!dfile.getParentFile().exists()){
					dfile.getParentFile().mkdirs();
				}
				write(bi,formatname,dfile);
			}else{
				for(File file:sfile.listFiles()){
					//文件夹中的图像名称不变化
					resize(file,new File(dfile,file.getName()),formatname,max);
				}
			}
		}
	}
	/**
	 * 给指定图像加水印
	 * @param bi 需要加水印的图像
	 * @param water 水印图像
	 * @param left 水印的左坐标
	 * @param top 水印的上坐标
	 * @return 处理后的图像
	 */
	public static BufferedImage addWater(BufferedImage bi,Image water,int left,int top){
		bi.getGraphics().drawImage(water, left, top, null);
		return bi;
	}
	/**
	 * 给指定图像正中加水印
	 * @param bi 需要加水印的图像
	 * @param water 水印图像
	 * @return 处理后的图像
	 */
	public static BufferedImage addWater(BufferedImage bi,Image water){
		int left = 0;
		int top = 0;
		left = (bi.getWidth()-water.getWidth(null))/2;
		top = (bi.getHeight()-water.getHeight(null))/2;
		left = left<0?0:left;
		top = top<0?0:top;
		ImageUtil.addWater(bi, water, left, top);
		return bi;
	}
	/**
	 * 图像克隆
	 * @param bi 需要处理的图像
	 * @param imageType 图像类型
	 * @return 克隆后的图像
	 */
	public static BufferedImage clone(BufferedImage bi,int imageType){
		int width = bi.getWidth();
		int height = bi.getHeight();
		BufferedImage nbi = new BufferedImage(width,height,imageType);
		nbi.getGraphics().drawImage(bi, 0, 0, width, height, null);
		return nbi;
	}
	public static void main(String []args) throws IOException, Exception{
		BufferedImage bi = read("1pic.jpg");
		bi = resize(bi,400);
		write(bi,"jpeg","resize.jpg");
	}
}
